package com.chauhai.android.batsg.mymap;

import java.io.IOException;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.RelativeLayout.LayoutParams;

/**
 * Base class for the location displayed on the map.
 */
public class MapLocation extends ImageView {

  /**
   * Default image to display the location on the map.
   * This is a singleton variable.
   */
  protected static Bitmap defaultImage;

  /**
   * The position of the MapLocation on the map.
   */
  protected Position position = new Position(0, 0);

  /**
   * Set this to allow zoomable.
   */
  private Position originalSize = null;

  /**
   * Constructor.
   * @param context
   */
  public MapLocation(Context context) {
    super(context);

    // Set layout to wrap content.
    LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    setLayoutParams(params);

    // Set the default location image.
    setDefaultLocationImage();
  }

  /**
   * Specify the position on the map.
   * @param context
   * @param x
   * @param y
   */
  public MapLocation(Context context, int x, int y) {
    this(context);
    position.set(x, y);
  }

  /**
   * Construct the object from an XML file. Valid Attributes:
   * <ul>
   *   <li><em>position_x</em> and <em>position_y</em></li>
   *   <li><em>android:src</em></li>
   *   <li><em>original_width</em> and <em>original_height</em></li>
   * </ul>
   * @see android.widget.ImageView#ImageView(android.content.Context,
   *    android.util.AttributeSet)
   * @param context The Context the view is running in,
   *     through which it can access the current theme, resources, etc.
   * @param attrs The attributes of the XML tag that is inflating the view.
   */
  public MapLocation(Context context, AttributeSet attrs) {
    super(context, attrs);

    // Set the default location image if android:src is not set.
    if (getDrawable() == null) {
      setDefaultLocationImage();
    }

    // Get the data from the XML AttributeSet.
    TypedArray a = context.obtainStyledAttributes(attrs,
        R.styleable.MapLocation);

    // Set position via position_x, position_y.
    int x = a.getInt(R.styleable.MapLocation_position_x, 0);
    int y = a.getInt(R.styleable.MapLocation_position_y, 0);
    position.set(x, y);

    // Set originalSize via original_width, original_height.
    int w = a.getInt(R.styleable.MapLocation_original_width, -1);
    int h = a.getInt(R.styleable.MapLocation_original_height, -1);
    if (w > 0 && h > 0) {
      setZoomable(w, h);
    }

    a.recycle();
  }

  /**
   * Using image in resources to set state.
   * @param context
   * @param x
   * @param y
   * @param normalDrawableId
   * @param pressedDrawableId
   */
  public MapLocation(Context context, int x, int y,
      int normalDrawableId, int pressedDrawableId) {
    this(context, x, y);
    // Set state.
    setState(normalDrawableId, pressedDrawableId);
  }

  /**
   * Using image in assets folder to set state.
   * @param context
   * @param x
   * @param y
   * @param normalImage
   * @param pressedImage
   */
  public MapLocation(Context context, int x, int y,
      String normalImage, String pressedImage) {
    this(context, x, y);
    // Set state.
    setState(normalImage, pressedImage);
  }

  /**
   * Setter
   * @param position
   */
  protected void setPosition(Position position) {
    this.position.set(position.x, position.y);
  }

  /**
   * Getter.
   * @return
   */
  public Position getPosition() {
    return position;
  }

  /**
   * Allow this View resizable when zooming.
   * @param originalWidth
   * @param originalHeight
   */
  public void setZoomable(int originalWidth, int originalHeight) {
    this.originalSize = new Position(originalWidth, originalHeight);
  }

  /**
   * Allow this View resizable when zooming.
   * @param originalSize
   */
  public void setZoomable(Position originalSize) {
    setZoomable(originalSize.x, originalSize.y);
  }

  protected void setDefaultLocationImage() {
    setImageBitmap(getDefaultImage());
  }

  /**
   * Get the default image (a red circle).
   * @return
   */
  protected static Bitmap getDefaultImage() {
    if (defaultImage == null) {
      Bitmap bitmap = Bitmap.createBitmap(20, 20, Config.ARGB_8888);
      Paint paint = new Paint();
      paint.setColor(Color.RED);
      paint.setAlpha(0xAA); // Half transparent
      Canvas canvas = new Canvas(bitmap);
      canvas.drawCircle(5, 5, 5, paint);
      defaultImage = bitmap;
    }
    return defaultImage;
  }

  /**
   * Zoom the MapLocation according to the zooming of the map.
   * This will resize the size and adjust the position.
   * @param zoomRatio
   * @param mapImageLeftTop
   */
  public void zoom(float zoomRatio, Position mapImageLeftTop) {
    if (zoomRatio > 0) {
      // Zoomed size.
      Position newSize = calcZoomedSize(zoomRatio);
      // New position depends on the zoom ratio and mapImage left-top.
      Position position = getPosition().mult(zoomRatio).add(mapImageLeftTop);
      // Reset size and left-top position.
      LayoutParams params = originalSize != null ?
          new LayoutParams(newSize.x, newSize.y) :
          (LayoutParams) getLayoutParams();
      params.leftMargin = position.x - newSize.x / 2;
      params.topMargin = position.y - newSize.y / 2;
      setLayoutParams(params);
    }
  }

  protected Position calcZoomedSize(float zoomRatio) {
    // Try to get original size.
    // If originalSize is not null, then it is used, else the current size is return.
    Position newSize = originalSize != null ?
        originalSize.mult(zoomRatio) :
        new Position(getWidth(), getHeight());
    return newSize;
  }

  /**
   * Set state using drawables in resource.
   * @param normalResId Drawable resource in normal.
   * @param pressedResId Drawable resource when pressing.
   */
  public void setState(int normalResId, int pressedResId) {
    StateListDrawable drawable = new StateListDrawable();
    drawable.addState(new int[] {android.R.attr.state_pressed}, getResources().getDrawable(pressedResId));
    drawable.addState(new int[] {android.R.attr.state_enabled}, getResources().getDrawable(normalResId));
    setImageDrawable(drawable);
  }

  /**
   * Set state using images in assets folder.
   * @param normalImage Image in normal.
   * @param pressedImage Image in pressing.
   */
  public void setState(String normalImage, String pressedImage) {
    try {
      StateListDrawable drawable = new StateListDrawable();
      drawable.addState(new int[] {android.R.attr.state_pressed}, loadDrawable(pressedImage));
      drawable.addState(new int[] {android.R.attr.state_enabled}, loadDrawable(normalImage));
      setImageDrawable(drawable);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  /**
   * Create a Drawable from an image file in the assets folder.
   * @param fileName
   * @return
   * @throws IOException
   */
  private Drawable loadDrawable(String fileName) throws IOException {
    return Drawable.createFromStream(getContext().getAssets().open(fileName), null);
  }

  @Override
  public String toString() {
    return position.toString();
  }
}